package com.xianyu.admin.autogenerator;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.GeneratedKey;
import org.mybatis.generator.config.JDBCConnectionConfiguration;
import org.mybatis.generator.config.JavaClientGeneratorConfiguration;
import org.mybatis.generator.config.JavaModelGeneratorConfiguration;
import org.mybatis.generator.config.ModelType;
import org.mybatis.generator.config.PluginConfiguration;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.internal.DefaultShellCallback;

import com.google.common.base.CaseFormat;
import com.xianyu.admin.common.ProjectConstant;

import freemarker.template.TemplateExceptionHandler;

/**
 * 代码生成器 V2.0
 * 根据数据表名称生成对应的Model、Mapper、Service、Controller简化开发。
 * 作者:羡鱼
 * qq:642133706
 * 当前时间 2024/8/31 8:14
 */
public class MyBatisCodeGeneratorV2 {

    private static Connection connection = null;
    private static ResultSet rs = null;

    public static void main(String[] args) {
        //genCodeByCustomModelName("输入表名","输入自定义Model名称");
        try {
            genCode("xianyu_user");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过数据表名称生成代码，Model 名称通过解析数据表名称获得，下划线转大驼峰的形式。
     * 如输入表名称 "t_user_detail" 将生成 TUserDetail、TUserDetailMapper、TUserDetailService ...
     *
     * @param tableNames 数据表名称...
     */
    public static void genCode(String... tableNames) throws SQLException {
        for (String tableName : tableNames) {
            genCodeByCustomModelName(tableName);
        }
    }

    /**
     * 通过数据表名称，和自定义的 Model 名称生成代码
     * 如输入表名称 "t_user_detail" 和自定义的 Model 名称 "User" 将生成 User、UserMapper、UserService ...
     *
     * @param tableName 数据表名称
     */
    public static void genCodeByCustomModelName(String tableName) throws SQLException {
        genModelAndMapper(tableName);
        genService(tableName);
        genController(tableName);
    }

    public static void genModelAndMapper(String tableName) throws SQLException {

        Context context = new Context(ModelType.FLAT);
        context.setId("Potato");
        context.setTargetRuntime("MyBatis3Simple");
        context.addProperty(PropertyRegistry.CONTEXT_BEGINNING_DELIMITER, "`");
        context.addProperty(PropertyRegistry.CONTEXT_ENDING_DELIMITER, "`");

        JDBCConnectionConfiguration jdbcConnectionConfiguration = new JDBCConnectionConfiguration();
        jdbcConnectionConfiguration.setConnectionURL(ProjectConstant.JDBC_URL);
        jdbcConnectionConfiguration.setUserId(ProjectConstant.JDBC_USERNAME);
        jdbcConnectionConfiguration.setPassword(ProjectConstant.JDBC_PASSWORD);
        jdbcConnectionConfiguration.setDriverClass(ProjectConstant.JDBC_DIVER_CLASS_NAME);
        context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);

        PluginConfiguration pluginConfiguration = new PluginConfiguration();
        pluginConfiguration.setConfigurationType("tk.mybatis.mapper.generator.MapperPlugin");
        pluginConfiguration.addProperty("mappers", ProjectConstant.MAPPER_INTERFACE_REFERENCE);
        context.addPluginConfiguration(pluginConfiguration);

        JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();
        javaModelGeneratorConfiguration.setTargetProject(ProjectConstant.PROJECT_PATH + ProjectConstant.JAVA_PATH);
        javaModelGeneratorConfiguration.setTargetPackage(ProjectConstant.MODEL_PACKAGE);
        context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);

        SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration = new SqlMapGeneratorConfiguration();
        sqlMapGeneratorConfiguration.setTargetProject(ProjectConstant.PROJECT_PATH + ProjectConstant.RESOURCES_PATH);
        sqlMapGeneratorConfiguration.setTargetPackage("mybatis/mapper");
        context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);

        JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();
        javaClientGeneratorConfiguration.setTargetProject(ProjectConstant.PROJECT_PATH + ProjectConstant.JAVA_PATH);
        javaClientGeneratorConfiguration.setTargetPackage(ProjectConstant.MAPPER_PACKAGE);
        javaClientGeneratorConfiguration.setConfigurationType("XMLMAPPER");
        context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);

        TableConfiguration tableConfiguration = new TableConfiguration(context);
        tableConfiguration.setTableName(tableName);
        //        if (StringUtils.isNotEmpty(modelName))
        //            tableConfiguration.setDomainObjectName(modelName);
        tableConfiguration.setGeneratedKey(new GeneratedKey("id", "Mysql", true, null));
        context.addTableConfiguration(tableConfiguration);

        List<String> warnings;
        MyBatisGenerator generator;
        try {
            Configuration config = new Configuration();
            config.addContext(context);
            config.validate();

            boolean overwrite = true;
            DefaultShellCallback callback = new DefaultShellCallback(overwrite);
            warnings = new ArrayList<>();
            generator = new MyBatisGenerator(config, callback, warnings);
            generator.generate(null);
        } catch (Exception e) {
            throw new RuntimeException("生成Model和Mapper失败", e);
        }

        if (generator.getGeneratedJavaFiles().isEmpty() || generator.getGeneratedXmlFiles().isEmpty()) {
            throw new RuntimeException("生成Model和Mapper失败：" + warnings);
        }
        try {
            freemarker.template.Configuration cfg = getConfiguration();
            Map<String, Object> data = new HashMap<>();
            data.put("tableName", tableName);
            data.put("entity", com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName));
            data.put("basePackage", ProjectConstant.BASE_PACKAGE);

            Class.forName(ProjectConstant.JDBC_DIVER_CLASS_NAME);
            connection = DriverManager.getConnection(ProjectConstant.JDBC_URL, ProjectConstant.JDBC_USERNAME, ProjectConstant.JDBC_PASSWORD);
            String sql = "select column_name,data_type,column_key from information_schema.columns where table_schema = (select database()) and table_name=?";
            PreparedStatement ps = connection.prepareStatement(sql);
            ps.setString(1, tableName);
            rs = ps.executeQuery();
            Map<String, Object> map = new HashMap<>();
            while (rs.next()) {
                if ("PRI".equals(rs.getString("column_key"))) {
                    data.put("ano", "@Id\n" +
                            "    @GeneratedValue(strategy = GenerationType.IDENTITY)");
                }
                map.put(com.xianyu.admin.utils.StringUtils.camelCaseName(rs.getString("column_name")), com.xianyu.admin.utils.StringUtils.typeMapping(rs.getString("data_type")));
                data.put("maps", map);
            }
            File file = new File(ProjectConstant.PROJECT_PATH + ProjectConstant.JAVA_PATH + ProjectConstant.PACKAGE_PATH_MODEL + com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName) + ".java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("model.ftl").process(data, new FileWriter(file));
            rs.close();
            connection.close();
        } catch (Exception e) {
            throw new RuntimeException("生成Model失败", e);
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
        //        if (StringUtils.isEmpty(modelName))
        //            modelName = com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName);
        System.out.println(com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName) + ".java 生成成功");
        System.out.println(com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName) + "Mapper.java 生成成功");
        System.out.println(com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName) + "Mapper.xml 生成成功");
    }

    public static void genService(String tableName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            data.put("date", ProjectConstant.DATE);
            String modelNameUpperCamel = com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName);
            data.put("modelNameUpperCamel", modelNameUpperCamel);
            data.put("modelNameLowerCamel", com.xianyu.admin.utils.StringUtils.tableNameConvertLowerCamel(tableName));
            data.put("basePackage", ProjectConstant.BASE_PACKAGE);

            File file = new File(ProjectConstant.PROJECT_PATH + ProjectConstant.JAVA_PATH + ProjectConstant.PACKAGE_PATH_SERVICE + modelNameUpperCamel + "Service.java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("service.ftl").process(data, new FileWriter(file));
            System.out.println(modelNameUpperCamel + "Service.java 生成成功");

            File file1 = new File(ProjectConstant.PROJECT_PATH + ProjectConstant.JAVA_PATH + ProjectConstant.PACKAGE_PATH_SERVICE_IMPL + modelNameUpperCamel + "ServiceImpl.java");
            if (!file1.getParentFile().exists()) {
                file1.getParentFile().mkdirs();
            }
            cfg.getTemplate("service-impl.ftl").process(data,
                    new FileWriter(file1));
            System.out.println(modelNameUpperCamel + "ServiceImpl.java 生成成功");
        } catch (Exception e) {
            throw new RuntimeException("生成Service失败", e);
        }
    }

    public static void genController(String tableName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            data.put("date", ProjectConstant.DATE);
            String modelNameUpperCamel = com.xianyu.admin.utils.StringUtils.tableNameConvertUpperCamel(tableName);
            data.put("baseRequestMapping", com.xianyu.admin.utils.StringUtils.modelNameConvertMappingPath(modelNameUpperCamel));
            data.put("modelNameUpperCamel", modelNameUpperCamel);
            data.put("modelNameLowerCamel", CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, modelNameUpperCamel));
            data.put("basePackage", ProjectConstant.BASE_PACKAGE);

            File file = new File(ProjectConstant.PROJECT_PATH + ProjectConstant.JAVA_PATH + ProjectConstant.PACKAGE_PATH_CONTROLLER + modelNameUpperCamel + "Controller.java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("controller.ftl").process(data, new FileWriter(file));

            System.out.println(modelNameUpperCamel + "Controller.java 生成成功");
        } catch (Exception e) {
            throw new RuntimeException("生成Controller失败", e);
        }

    }

    private static freemarker.template.Configuration getConfiguration() throws IOException {
        freemarker.template.Configuration cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_23);
        cfg.setDirectoryForTemplateLoading(new File(ProjectConstant.TEMPLATE_FILE_PATH));
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        return cfg;
    }

}
